////////////////////////////////////////////////////////////////////////////////
// Filename: systemclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "SystemClass.h"

SystemClass::SystemClass()
	:m_applicationName(""),
	m_hinstance(0),
	m_hwnd(0),
	m_resourceManager(),
	m_Input(),
	m_Graphics(),
	m_Manager(),
	m_Log(),
	m_Logic(),
	m_Config(),
	m_Control(),
	m_camera(),
	m_timer()
{
}

SystemClass::~SystemClass()
{
	ShutdownWindows();
}


void SystemClass::InitObjects()
{
	m_Manager=boost::make_shared<Manager>();
	m_Manager->AddObject(boost::make_shared<Object>(OBJ_HID));
	m_Manager->AddObject(boost::make_shared<Object>(OBJ_CORE));
	m_Manager->AddObject(boost::make_shared<Object>(OBJ_RESOURCE));
	m_Manager->AddObject(boost::make_shared<Object>(OBJ_GRAPHICS));
	m_Manager->AddObject(boost::make_shared<Object>(OBJ_GAME));
	m_Manager->AddObject(boost::make_shared<Object>(OBJ_ANIM));
	m_Manager->AddObject(boost::make_shared<Object>(OBJ_CAMERA));
	m_Manager->AddObject(boost::make_shared<Object>(OBJ_SOUND));
}

bool SystemClass::Initialize()
{
	try
	{
		int screenWidth=0;
		int screenHeight=0;
		bool fullScreen=false;
		bool vsync=false;
		string resMap;
		// Initialize the width and height of the screen to zero before sending the variables into the function.
		screenWidth = 0;
		screenHeight = 0;
		
		InitObjects();
		
		m_Config=boost::make_shared<Config>();
		m_Manager->AddComponent(OBJ_CORE,m_Config);
		
		screenWidth=m_Config->m_screenWidth;
		screenHeight=m_Config->m_screenHeight;
		fullScreen=m_Config->m_fullScreen;
		vsync=m_Config->m_vsyncEnable;
		resMap=m_Config->m_resMap;
		if(resMap=="")
		{
			EXCEPTION("Resource Mapping file not found")
		}
		// Initialize the windows api.
		InitializeWindows(screenWidth, screenHeight, fullScreen);

		// Create the input object.  This object will be used to handle reading the keyboard input from the user.

		m_Input=boost::make_shared<InputClass>();
		m_Manager->AddComponent(OBJ_HID,m_Input);
		
		m_Control=boost::make_shared<Control>();
		m_Manager->AddComponent(OBJ_GAME,m_Control);

		m_timer=boost::make_shared<Timer>();
		m_Manager->AddComponent(OBJ_CORE,m_timer);
		
		// Create the graphics object.  This object will handle rendering all the graphics for this application.
		m_Graphics=boost::make_shared<GraphicsClass>(fullScreen,vsync);
		m_Manager->AddComponent(OBJ_GRAPHICS,m_Graphics);

		m_resourceManager=boost::make_shared<ResourceManager>(resMap);
		m_Manager->AddComponent(OBJ_RESOURCE,m_resourceManager);

		m_Log=boost::make_shared<Log>();
		m_Manager->AddComponent(OBJ_CORE,m_Log);
		
		m_Logic=boost::make_shared<Logic>();
		m_Manager->AddComponent(OBJ_GAME,m_Logic);

		m_camera=boost::make_shared<CameraClass>();
		m_Manager->AddComponent(OBJ_CORE,m_camera);

		m_Manager->SendMessage(MSG_INITIAL,AuxGroupArgus(m_hinstance,m_hwnd,screenWidth,screenHeight));
		m_Manager->SendMessage(MSG_INITIAL_GRAPHICS,AuxGroupArgus(m_hinstance,m_hwnd,screenWidth,screenHeight));
		m_Manager->SendMessage(MSG_INITIAL_GAME_DATA,boost::any());
	}
	catch(std::exception& e)
	{
		PRINT_ERROR(e);
	}
	
   	return true;
}

void SystemClass::Run()
{
	MSG msg;
	bool done, result;


	// Initialize the message structure.
	ZeroMemory(&msg, sizeof(MSG));

	// Loop until there is a quit message from the window or the user.
	done = false;
	while(!done)
	{
		// Handle the windows messages.
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		// If windows signals to end the application then exit out.
		if(msg.message == WM_QUIT)
		{
			done = true;
		}
		else
		{
			// Otherwise do the frame processing.
			result = Frame();
			if(!result)
			{
				done = true;
			}
		}

	}

	return;
}

/*
Process of each frame.
1. Get the human input.
	Control class will send message according to specificed input.
2. Logic update per frame.
3. Graphic update pre frame.
	Graphic will update the resource first.
	Then render all object one by one.
*/

bool SystemClass::Frame()
{
	bool result;

	
	m_timer->Update();

	result = m_Control->Frame();
	if(!result)
	{
		return false;
	}
	result = m_Control->Frame();
	
	if(!result)
	{
		return false;
	}

	
	result = m_Logic->Frame();
	if(!result)
	{
		return false;
	}
	
	// Do the frame processing for the graphics object.
	result = m_Graphics->Frame();
	if(!result)
	{
		return false;
	}	
	return true;
}


LRESULT CALLBACK SystemClass::MessageHandler(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
	return DefWindowProc(hwnd, umsg, wparam, lparam);
}


void SystemClass::InitializeWindows(int& screenWidth, int& screenHeight, bool fullScreen)
{
	WNDCLASSEX wc;
	DEVMODE dmScreenSettings;
	int posX, posY;


	// Get an external pointer to this object.	
	ApplicationHandle = this;

	// Get the instance of this application.
	m_hinstance = GetModuleHandle(NULL);

	// Give the application a name.
	m_applicationName = "Engine";

	// Setup the windows class with default settings.
	wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc   = WndProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = m_hinstance;
	wc.hIcon		 = LoadIcon(NULL, IDI_WINLOGO);
	wc.hIconSm       = wc.hIcon;
	wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = m_applicationName.c_str();
	wc.cbSize        = sizeof(WNDCLASSEX);

	// Register the window class.
	RegisterClassEx(&wc);

	// Setup the screen settings depending on whether it is running in full screen or in windowed mode.
	if(fullScreen)
	{
	
		// Determine the resolution of the clients desktop screen.
		screenWidth  = GetSystemMetrics(SM_CXSCREEN);
		screenHeight = GetSystemMetrics(SM_CYSCREEN);
		
		// If full screen set the screen to maximum size of the users desktop and 32bit.
		memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
		dmScreenSettings.dmSize       = sizeof(dmScreenSettings);
		dmScreenSettings.dmPelsWidth  = (unsigned long)screenWidth;
		dmScreenSettings.dmPelsHeight = (unsigned long)screenHeight;
		dmScreenSettings.dmBitsPerPel = 32;			
		dmScreenSettings.dmFields     = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		// Change the display settings to full screen.
		ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);

		// Set the position of the window to the top left corner.
		posX = posY = 0;
	}
	else
	{
		// Place the window in the middle of the screen.
		posX = (GetSystemMetrics(SM_CXSCREEN) - screenWidth)  / 2;
		posY = (GetSystemMetrics(SM_CYSCREEN) - screenHeight) / 2;
	}

	// Create the window with the screen settings and get the handle to it.
	m_hwnd = CreateWindowEx(WS_EX_APPWINDOW, m_applicationName.c_str(), m_applicationName.c_str(), 
		WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP,
		posX, posY, screenWidth, screenHeight, NULL, NULL, m_hinstance, NULL);

	// Bring the window up on the screen and set it as main focus.
	ShowWindow(m_hwnd, SW_SHOW);
	SetForegroundWindow(m_hwnd);
	SetFocus(m_hwnd);

	// Hide the mouse cursor.
	ShowCursor(false);

	return;
}


void SystemClass::ShutdownWindows()
{
	// Show the mouse cursor.
	ShowCursor(true);

	// Fix the display settings if leaving full screen mode.
	/*if(m_full)
	{
		ChangeDisplaySettings(NULL, 0);
	}
	*/
	// Remove the window.
	DestroyWindow(m_hwnd);
	m_hwnd = NULL;

	// Remove the application instance.
	UnregisterClass(m_applicationName.c_str(), m_hinstance);
	m_hinstance = NULL;

	// Release the pointer to this class.
	ApplicationHandle = NULL;

	return;
}


LRESULT CALLBACK WndProc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
{
	switch(umessage)
	{
		// Check if the window is being destroyed.
	case WM_DESTROY:
		{
			PostQuitMessage(0);
			return 0;
		}

		// Check if the window is being closed.
	case WM_CLOSE:
		{
			PostQuitMessage(0);		
			return 0;
		}

		// All other messages pass to the message handler in the system class.
	default:
		{
			return ApplicationHandle->MessageHandler(hwnd, umessage, wparam, lparam);
		}
	}
}